﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Input;

namespace GMUtilitiesDotNet.Gestures
{
    /// <summary>
    /// Listens UI events of UIElement and dispatches them to list of gestures.
    /// </summary>
    class UiEventsDispatcher
    {
        /// <summary>
        /// UIElement from which UI events are listened
        /// </summary>
        private UIElement UiElement { get; set; }

        /// <summary>
        /// Gesture list to which UI events are dispatched
        /// </summary>
        private IList<GestureCommand> GestureList { get; set; }

        public UiEventsDispatcher(IList<GestureCommand> gestureList)
        {
            GestureList = gestureList;
        }

        /// <summary>
        /// Subscribes to UIElement for event listening
        /// </summary>
        /// <param name="uiElement"></param>
        public void Subscribe(UIElement uiElement)
        {
            UiElement = uiElement;

            UnsubscribeFromEvents(uiElement);
            SubscribeToEvents(uiElement);
        }

        /// <summary>
        /// Unsubscribes from UIElement
        /// </summary>
        /// <param name="uiElement"></param>
        public void Unsubscribe(UIElement uiElement)
        {
            System.Diagnostics.Debug.Assert(object.ReferenceEquals(uiElement, UiElement));

            UnsubscribeFromEvents(uiElement);

            UiElement = null;
        }

        /// <summary>
        /// Subscribes to UIElement's mouse and keyboard events
        /// </summary>
        /// <param name="uiElement"></param>
        private void UnsubscribeFromEvents(UIElement uiElement)
        {
            uiElement.MouseDown -= OnMouseDown;
            uiElement.MouseUp -= OnMouseUp;
            uiElement.MouseLeftButtonDown -= OnMouseLeftButtonDown;
            uiElement.MouseLeftButtonUp -= OnMouseLeftButtonUp;
            uiElement.MouseRightButtonDown -= OnMouseRightButtonDown;
            uiElement.MouseRightButtonUp -= OnMouseRightButtonUp;
            uiElement.MouseMove -= OnMouseMove;
            uiElement.MouseWheel -= OnMouseWheel;
            uiElement.MouseEnter -= OnMouseEnter;
            uiElement.MouseLeave -= OnMouseLeave;

            uiElement.KeyDown -= OnKeyDown;

            uiElement.LostFocus -= OnLostFocus;
        }

        /// <summary>
        /// Unsubscribes from UIElement's mouse and keyboard events
        /// </summary>
        /// <param name="uiElement"></param>
        private void SubscribeToEvents(UIElement uiElement)
        {
            uiElement.MouseDown += OnMouseDown;
            uiElement.MouseUp += OnMouseUp;
            uiElement.MouseLeftButtonDown += OnMouseLeftButtonDown;
            uiElement.MouseLeftButtonUp += OnMouseLeftButtonUp;
            uiElement.MouseRightButtonDown += OnMouseRightButtonDown;
            uiElement.MouseRightButtonUp += OnMouseRightButtonUp;
            uiElement.MouseMove += OnMouseMove;
            uiElement.MouseWheel += OnMouseWheel;
            uiElement.MouseEnter += OnMouseEnter;
            uiElement.MouseLeave += OnMouseLeave;

            uiElement.KeyDown += OnKeyDown;

            uiElement.LostFocus += OnLostFocus;
        }

        /// <summary>
        /// Tests all gestures
        /// </summary>
        /// <param name="gestureHandler">Handler to handle specific gesture event</param>
        /// <returns>True if at least one gesture has handled gesture event</returns>
        private bool TestAllGestures(Predicate<IGesture> gestureHandler)
        {
            bool isAnyGestureHandled = false;

            foreach (GestureCommand gestureCommand in GestureList)
            {
                isAnyGestureHandled |= gestureCommand.TestGesture(gestureHandler);
            }

            return isAnyGestureHandled;
        }

        #region UI events

        private void OnLostFocus(object sender, RoutedEventArgs args)
        {
            args.Handled = TestAllGestures((gesture) => gesture.LostFocus());
        }

        private void OnKeyDown(object sender, KeyEventArgs args)
        {
            args.Handled = TestAllGestures((gesture) => gesture.KeyboardInput(args));
        }

        private void OnMouseDown(object sender, MouseButtonEventArgs args)
        {
            args.Handled = TestAllGestures((gesture) => gesture.OnMouseDown(args));
        }

        private void OnMouseUp(object sender, MouseButtonEventArgs args)
        {
            args.Handled = TestAllGestures((gesture) => gesture.OnMouseUp(args));
        }

        private void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs args)
        {
            args.Handled = TestAllGestures((gesture) => gesture.OnMouseLeftButtonDown(args));
        }

        private void OnMouseLeftButtonUp(object sender, MouseButtonEventArgs args)
        {
            args.Handled = TestAllGestures((gesture) => gesture.OnMouseLeftButtonUp(args));
        }

        private void OnMouseRightButtonDown(object sender, MouseButtonEventArgs args)
        {
            args.Handled = TestAllGestures((gesture) => gesture.OnMouseRightButtonDown(args));
        }

        private void OnMouseRightButtonUp(object sender, MouseButtonEventArgs args)
        {
            args.Handled = TestAllGestures((gesture) => gesture.OnMouseRightButtonUp(args));
        }

        private void OnMouseMove(object sender, MouseEventArgs args)
        {
            args.Handled = TestAllGestures((gesture) => gesture.OnMouseMove(args));
        }

        private void OnMouseWheel(object sender, MouseWheelEventArgs args)
        {
            args.Handled = TestAllGestures((gesture) => gesture.OnMouseWheel(args));
        }

        private void OnMouseEnter(object sender, MouseEventArgs args)
        {
            args.Handled = TestAllGestures((gesture) => gesture.OnMouseEnter(args));
        }

        private void OnMouseLeave(object sender, MouseEventArgs args)
        {
            args.Handled = TestAllGestures((gesture) => gesture.OnMouseLeave(args));
        }

        #endregion
    }
}
